//
//  asyn.cpp
//  asyn
//
//  Created by Eduardo Poyart on 2/6/12.
//  Copyright (c) 2012 Eduardo Poyart. All rights reserved.
//

#include "asyn.h"

#include <ctime>
#include <iostream>
#include <string>
#include <vector>
#include <boost/array.hpp>
#include <boost/bind.hpp>
#include <boost/asio.hpp>

using boost::asio::ip::tcp;
using boost::asio::ip::udp;
using namespace std;

ANManager::ANManager()
{
    _ioService = new boost::asio::io_service();
}

ANManager::~ANManager()
{
    delete _ioService;
}

void ANManager::update()
{
    _ioService->reset();
    boost::system::error_code error;
    _ioService->poll(error);
    if (error)
    {
        cout << "Update: error " << error.message() << endl;
    }
}

void ANConnection::receiveCopy(std::vector<char>& dest)
{
    if (!_dataReady)
    {
        cout << "receiveCopy: data not ready" << endl;
        dest.clear();
        return;
    }
    dest = _buffer;
    _dataReady = false;
    read();
}

// -----------------------------------------

ANTcpServer::ANTcpServer(ANManager* manager, int port, int bufferSize):
    ANConnection(manager)
{
    _buffer.resize(bufferSize);
    _acceptor = new tcp::acceptor(*manager->_ioService, tcp::endpoint(tcp::v4(), (unsigned short)port));
    _socket = new tcp::socket(*manager->_ioService);
    _acceptor->async_accept(*_socket, boost::bind(&ANTcpServer::handleAccept,
                                                  this, boost::asio::placeholders::error));
}

void ANTcpServer::handleAccept(const boost::system::error_code& error)
{
    if (error)
    {
        cout << "TcpServer Accept: error " << error.message() << endl;
        return;
    }
    _state = CONNECTED;
    read();
}

void ANTcpServer::send(const std::string& message)
{
    boost::asio::async_write(*_socket, boost::asio::buffer(message),
                             boost::bind(&ANTcpServer::handleWrite, this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));
}

void ANTcpServer::handleWrite(const boost::system::error_code& error, size_t bytesTransferred)
{
    if (error)
    {
        cout << "TcpServer Write: error " << error.message() << endl;
    }
}

void ANTcpServer::read()
{
    _socket->async_read_some(boost::asio::buffer(_buffer, _buffer.size()),
                             boost::bind(&ANTcpServer::handleRead, this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));
}

void ANTcpServer::handleRead(const boost::system::error_code& error, size_t bytesTransferred)
{
    if (error == boost::asio::error::eof)
    {
        cout << "TcpServer: connection closed" << endl;
        _state = CONNECTION_CLOSED;
        return;
    }
    if (error)
    {
        cout << "TcpServer Read: error " << error.message() << endl;
        return;
    }
    _dataReady = true;
}

// -----------------------------------------

ANTcpClient::ANTcpClient(ANManager* manager, const std::string& host, const std::string& port, int bufferSize):
ANConnection(manager)
{
    _buffer.resize(bufferSize);
    
    _resolver = new tcp::resolver(*manager->_ioService);
    tcp::resolver::query query(host, port);
    _resolver->async_resolve(query,
                             boost::bind(&ANTcpClient::handleResolve, this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::iterator));    
}

void ANTcpClient::handleResolve(const boost::system::error_code& error, tcp::resolver::iterator iterator)
{
    if (error)
    {
        cout << "TcpClient Resolve: error " << error.message() << endl;
        return;
    }
    _socket = new tcp::socket(*_manager->_ioService);
    boost::asio::async_connect(*_socket, iterator,
                               boost::bind(&ANTcpClient::handleConnect, this,
                                           boost::asio::placeholders::error,
                                           boost::asio::placeholders::iterator));

}

void ANTcpClient::handleConnect(const boost::system::error_code& error, tcp::resolver::iterator iterator)
{
    if (error)
    {
        cout << "TcpClient Connect: error " << error.message() << endl;
        return;
    }
    _state = CONNECTED;
    read();
}


void ANTcpClient::send(const std::string& message)
{
    boost::asio::async_write(*_socket, boost::asio::buffer(message),
                             boost::bind(&ANTcpClient::handleWrite, this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));
}

void ANTcpClient::handleWrite(const boost::system::error_code& error, size_t bytesTransferred)
{
    if (error)
    {
        cout << "TcpClient Write: error " << error.message() << endl;
    }
}

void ANTcpClient::read()
{
    _socket->async_read_some(boost::asio::buffer(_buffer, 128),
                             boost::bind(&ANTcpClient::handleRead, this,
                                         boost::asio::placeholders::error,
                                         boost::asio::placeholders::bytes_transferred));
}

void ANTcpClient::handleRead(const boost::system::error_code& error, size_t bytesTransferred)
{
    if (error == boost::asio::error::eof)
    {
        cout << "TcpClient: connection closed" << endl;
        _state = CONNECTION_CLOSED;
        return;
    }
    if (error)
    {
        cout << "TcpClient Read: error " << error.message() << " " << error.value() << endl;
        return;
    }
    _dataReady = true;
}

